package animation.core;

import java.awt.*;

/**
 * Created by IntelliJ IDEA.
 * User: emilio
 * Date: Aug 17, 2004
 * Time: 10:48:01 PM
 * To change this template use File | Settings | File Templates.
 */
public abstract class AlgorithmAnimation
        implements Runnable, Cloneable
{
    protected boolean paused;
    protected int speed = 1;
    protected boolean stopRequested;
    private Thread thread;
    protected static Color BLUE = new Color(15, 20, 170);
    protected static Color LIGHT_BLUE = new Color(50, 150, 205);
    protected static Color RED = new Color(170, 20, 20);
    protected static Color LIGHT_RED = new Color(200, 100, 100);
    protected static Color GREEN = new Color(15, 170, 20);
    protected static Color LIGHT_GREEN = new Color(50, 205, 150);
    protected static Color MAIN = new Color(100, 150, 200);
    protected static Color LIGHT_MAIN = new Color(180, 200, 220);
    protected static Color TMAIN = new Color(100, 150, 200, 100);
    protected static Color LIGHT_TMAIN = new Color(180, 200, 220, 100);
    protected static final Stroke normalStroke = new BasicStroke(2);
    protected static int[] speedScale = {
        500, 1000, 750, 500, 300, 200, 150,
        100, 90, 75, 50, 30, 20, 15,
        10, 9, 7, 5, 3, 2, 1
    };

    static
    {
        assert speedScale.length == AnimationController.MAX_SPEED + 1 : "Bad Speed scale array";
    }

    protected boolean animating;
    private boolean painting;
    private boolean waitForPaintingToEnd;
    protected AnimationController animationController;

    public void run()
    {
        try {
            doRun();
            animationController.repaint();
        }
        catch (StopException e){
            // thread is supposed to die (as requested)
        }
        catch (Exception e) {
            System.err.println("Animating thread died unexpectedly!");
            e.printStackTrace();
        }
        animationController.end();
        thread = null;
    }

    protected abstract void doRun()
            throws Exception;

    protected abstract void doStart();

    public boolean supportsMonitoring(){
        return this instanceof Monitoreable;
    }

    public void start()
    {
        if (thread == null) {
            doStart();
            thread = new Thread(this);
            thread.start();
        }
    }

    public synchronized void doStop()
    {
        stopRequested = true;
        notify();
    }

    public void doSleep(int time)
            throws Exception
    {
        if (stopRequested) {
            throw new StopException();
        }
        beginPainting();
        animationController.repaint();
        doWait(time);
    }

    /**
     * @param time: microseconds at fastest speed, milliseconds at slowest
     */
    private synchronized void doWait(int time)
    {
        try {
            if (paused && !animating) {
                wait();
            }
            else if (speed == 0 && !animating) {
                animationController.pause();
                paused = true;
                wait();
            }
            else {
                int timeout = (time * speedScale[speed]) / 1000;
                if (timeout <= 0)
                    timeout = 1;
                wait(timeout);
               if (timeout > 20) {
                    while (painting) {
                        waitForPaintingToEnd = true;
                        wait();
                        waitForPaintingToEnd = false;
                    }
                }
            }
        }
        catch (InterruptedException e) {
        }
    }

    private void beginPainting()
    {
        painting = true;
    }

    protected synchronized void finishPainting()
    {
        painting = false;
        if (waitForPaintingToEnd) {
            notify();
        }

    }

    synchronized public void togglePause()
    {
        paused = !paused;
        if (!paused) {
            notify();
        }
    }

    synchronized public void step()
    {
        notify();
    }

    public void setSpeed(int speed)
    {
        assert speed >= 0 && speed <= AnimationController.MAX_SPEED;
        this.speed = speed;
    }

    @Override
    public AlgorithmAnimation clone() throws CloneNotSupportedException {
        return (AlgorithmAnimation)super.clone();    
    }

    private static class StopException extends Exception {

    }
}
